package com.gitee.wsl.time.statistics

import com.gitee.wsl.concurrent.collections.ConcurrentMutableList
import com.gitee.wsl.concurrent.collections.ConcurrentMutableMap
import timber.log.Timber
import kotlin.time.Duration
import kotlin.time.Duration.Companion.nanoseconds

open class DefaultTaskStatisticsCollector() : TaskStatisticsCollector {

    protected open val stats: ConcurrentMutableMap<String, ConcurrentMutableList<Duration>> = ConcurrentMutableMap()

    override fun addElapsedTime(task: String, elapsed: Duration) {
        stats.getOrPut(task) { ConcurrentMutableList() }.add(elapsed)
    }

    override fun getMeasuredDurationsFor(task: String): List<Duration>? {
        val taskStats = stats[task]

        return if (taskStats.isNullOrEmpty()) {
            null
        } else {
            taskStats // don't pass mutable state to the outside
        }
    }

    override fun getStatisticsFor(task: String): TaskStatistics? {
        return getMeasuredDurationsFor(task)?.let { measurements ->
            val measurementsNanos = measurements.map { it.inWholeNanoseconds }

            val min = measurementsNanos.minOrNull()!!
            val max = measurementsNanos.maxOrNull()!!
            val average = measurementsNanos.average().toLong()
            val total = measurementsNanos.sum()

            TaskStatistics(measurements, min.nanoseconds, max.nanoseconds, average.nanoseconds, total.nanoseconds)
        }
    }

    override fun logAllStatistics() {
        stats.keys.sorted().forEach { task -> logStatistics(task) }
    }

    override fun logStatistics(task: String) {
        getStatisticsFor(task)?.let { taskStats ->
            Timber.i(
                "$task [${taskStats.countMeasurements}]: " +
                        "min ${taskStats.min.toIsoString()}, " +
                        "avg ${taskStats.average.toIsoString()}, " +
                        "max ${taskStats.max.toIsoString()}, " +
                        "total ${taskStats.total.toIsoString()}"
            )
        }
    }

}